//Kathryn Glowinski
//SchedulePopulation.cpp
//Implementation of the SchedulePopulation class

#include <vector>
#include <string>
#include <cstdlib>
#include <iostream>
#include <ctime>
#include <QVector>
#include "schedulepopulation.h"
#include "scheduleindividual.h"

SchedulePopulation :: SchedulePopulation(int bitPopulationSize, int indivLength, ScheduleConflicts* sCons, int timeSlots){

        sPop = new vector<ScheduleIndividual*>();
        sFit = new vector<double>();

        vecSize = indivLength;
        popSize = bitPopulationSize;
        bestIndex = 0;

        sc = sCons;
        times = timeSlots;
}

int SchedulePopulation :: getPopSize(){
        return popSize;
}
int SchedulePopulation::getIndivSize(){
    return vecSize;
}

void SchedulePopulation :: populateRandomly(){

        for (int i = 0; i < popSize; i++){
                ScheduleIndividual* si = new ScheduleIndividual(vecSize, sc, times);
                si->populateRandomly();
                double siFit = si->getFitness();

                sPop->push_back(si);
                sFit->push_back(siFit);
        }
}

void SchedulePopulation :: populateFromVector(QVector<ScheduleIndividual*>* vec){

    for (int i = 0; i < popSize; i++){
        ScheduleIndividual* si = vec->at(i);
        double siFit = si->getFitness();

        sPop->push_back(si);
        sFit->push_back(siFit);
    }
}

double SchedulePopulation :: getBestFitness(){

        double best = 0.0;

        for (int i = 0; i < sFit->size(); i++){
                if (sFit->at(i) > best){
                        best = sFit->at(i);
                        bestIndex = i;
                }
        }
        return best;
}

string SchedulePopulation :: getBestGenes(){
        string bestGene = sPop->at(bestIndex)->print();
        return bestGene;
}

//returns the index of a random parent, with higher fitness more likely to be chosen
int SchedulePopulation :: randomParent(){

        double sum = 0.0;
        double sumFit = 0.0;

        for (int i = 0; i < popSize; i++){
                sum += sFit->at(i);
        }

        double rand = fRand(0, sum);

        for (int j = 0; j < popSize; j++){
                sumFit += sFit->at(j);

                //The random parent we generated is here
                if (sumFit > rand){
                        return j;
                }
        }
        //default return
        return popSize - 1;
}

double SchedulePopulation :: fRand(double fMin, double fMax){
    double f = (double)rand() / RAND_MAX;
    return fMin + f * (fMax - fMin);
}

SchedulePopulation* SchedulePopulation :: reproduce(){

        SchedulePopulation* newPop = new SchedulePopulation(popSize, vecSize, sc, times);

        for (int i = 0; i < popSize-1; i++){
                ScheduleIndividual* newS = createChild();
                double newSFit = newS->getFitness();
                newPop->sPop->push_back(newS);
                newPop->sFit->push_back(newSFit);
        }

        //Elitism
        //Note: we clone so that we can delete the original, and not have it laying around
        newPop->sPop->push_back(this->sPop->at(bestIndex)->clone());
        newPop->sFit->push_back(this->sFit->at(bestIndex));

        return newPop;
}
//This creates a child from two random parents
ScheduleIndividual* SchedulePopulation :: createChild(){

        ScheduleIndividual* newI = new ScheduleIndividual(vecSize, sc, times);

        int xover = rand() % vecSize;
        int parent1Index = randomParent();
        int parent2Index = randomParent();

        //Local copies of ScheduleIndivs
        ScheduleIndividual* parent1 = sPop->at(parent1Index);
        ScheduleIndividual* parent2 = sPop->at(parent2Index);

        newI->reproduce(parent1, parent2, xover);
        return newI;
}

//Destructor
SchedulePopulation :: ~SchedulePopulation(){

        //Need to remove inner ScheduleIndivs first
        for (int i = 0; i < popSize; i++){
                //calls the destructor for the scheduleindiv @ i
                delete sPop->at(i);
        }

        delete sPop;
        delete sFit;
}
